import wsgiref.util
import cgi
import urlparse
import datetime
import time
import pdb
import base64
import Cookie

import MSConfig; MSConfig.init('.')
import MSLogger; log = MSLogger.get('wsgi_server')
import MSDatabase
import MSWSGIPatterns as w
import MSObject
import MSObjects
import MSUser
import MSUsers
import MSDatetime
import ReaderMSAPI
import ReaderMSDB
import MSEvent

class MethodHelpers:
    def __init__(self): return
    def checkName(self, name):
        if w.checkName(name): return True
        if type(name) != type(str()): name = 'bad_type'
        log.debug('wrong name %s', name)
        return False

helpers = MethodHelpers()

class MethodHandler:
    #this class should not have any fields until methods are invoked directly by http request (using getattr)
    #otherwise internal fields could be accessed too
    def __init__(self): return

    def login(self, environ, start_response):
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        email   = form.getvalue('user_email')
        if not helpers.checkName(email):
            w.putHeader(start_response, 200, 'html', 'text')
            return ['failed']
        pwd     = form.getvalue('user_pwd')
        if not helpers.checkName(pwd): 
            w.putHeader(start_response, 200, 'html', 'text')
            return ['failed']

        uid     = MSUser.hash(email)
        user    = MSDatabase.getInstance().users.find(uid)
        if not user or (MSUser.hash(pwd) != user.storage.meta['pwd']):
            w.putHeader(start_response, 200, 'html', 'text')
            return ['failed']

        cookie = Cookie.SimpleCookie()
        cookie['id']    = uid
        sessionkey      = MSUser.hash(str(time.time()))
        cookie['session'] = sessionkey
        user.storage.meta['session_key'] = sessionkey
        user.flush()

        w.putHeader(start_response, 200, 'html', 'text', [('Set-Cookie', cookie.output(header=''))])
        return ['logged in']

    def logoff(self, environ, start_response):
        cookie = MSUser.cookie(environ)
        log.debug('got cookie: %s', cookie.output())
        null = Cookie.SimpleCookie()
        w.putHeader(start_response, 200, 'html', 'text', [('Set-Cookie', null.output(header=''))])
        return ['logged off']

    def register(self, environ, start_response):
        w.putHeader(start_response, 200, 'html', 'text')

        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        email   = form.getvalue('user_email')
        if not helpers.checkName(email): return ['failed - email is incorrect']
        pwd     = form.getvalue('user_pwd')
        if not helpers.checkName(pwd): return ['failed - pwd is incorrect']
        pwd2    = form.getvalue('user_pwd2')
        if not helpers.checkName(pwd2): return ['failed - pwd is incorrect']
        if MSUser.hash(pwd) != MSUser.hash(pwd2): return ['failed - pwd missmatch']

        realname = form.getvalue('user_realname')
        phone   = form.getvalue('user_phone')

        u = MSUser.MSUser(MSConfig.config.get('main','dbpath'), MSUser.hash(email))
        u.open()
        if u.storage.txn > 0: return ['failed - already exists']
        u.storage.meta['email'] = email
        u.storage.meta['pwd'] = MSUser.hash(pwd)
        u.storage.meta['realname'] = realname
        u.storage.meta['phone'] = phone
        u.flush()
        return ['success']

    def list_objects(self, environ, start_response):
        w.putHeader(start_response, 200, 'html', 'text')
        ret = []
        t   = w.PatternTable()
        info = w.PatternForm('get_object', 'Info', 'post')
        tail = w.PatternForm('get_object_events', 'Tail', 'post')
        photos = w.PatternForm('list_object_photos', 'Photos', 'post')
        ret += t.open([('#',    {}),
                       ('name', {}),
                       ('msId', {}),
                       ('pcnId',{}),
                       ('control',     {}),
                      ])
        c = 0
        for oid,obj in MSObjects.genObjects(MSConfig.config.get('main','dbpath')):
            btn_info =  info.open   ({'obj_name':   {'type':'input',
                                                     'desc':'Object Name',
                                                     'value':str(obj.objId),
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'class':'span8',
                                                             'id':'obj_name',
                                                             'name':'obj_name',
                                                             'size':30,
                                                             'type':'text',
                                                             'value':str(obj.objId)
                                                            }
                                                    },
                                      'obj_fromDisk':{'type':'input',
                                                     'desc':'Read Object From Disk',
                                                     'value':'',
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'type':'checkbox',
                                                             'name':'obj_fromDisk',
                                                             'value':'off'
                                                            }
                                                     }
                                    })[0]
            btn_tail =  tail.open   ({'obj_name':   {'type':'input',
                                                     'desc':'Object Name',
                                                     'value':str(obj.objId),
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'class':'span8',
                                                             'id':'obj_name',
                                                             'name':'obj_name',
                                                             'size':30,
                                                             'type':'text',
                                                             'value':str(obj.objId)
                                                            }
                                                    },
                                      'obj_startdate':{'type':'input',
                                                     'desc':'Start From Date',
                                                     'value':'',
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'class':'span8',
                                                             'id':'obj_startdate',
                                                             'name':'obj_startdate',
                                                             'size':30,
                                                             'type':'text',
                                                             'value':''
                                                            }
                                                    },

                                      'obj_usedb'   :{'type':'input',
                                                     'desc':'Use Car-Online DB',
                                                     'value':'',
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'type':'checkbox',
                                                             'name':'obj_usedb',
                                                            }
                                                     },
                                      'obj_useapi'  :{'type':'input',
                                                     'desc':'Use Car-Online API',
                                                     'value':'',
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'type':'checkbox',
                                                             'name':'obj_useapi',
                                                            }
                                                     },
                                      'obj_tail'    :{'type':'input',
                                                     'desc':'Tail events',
                                                     'value':'',
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'type':'checkbox',
                                                             'name':'obj_tail',
                                                             'checked':'checked'
                                                            }
                                                     },
                                    })[0]
            btn_photo = photos.open ({'obj_name':   {'type':'input',
                                                     'desc':'Object Name',
                                                     'value':str(obj.objId),
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'class':'span8',
                                                             'id':'obj_name',
                                                             'name':'obj_name',
                                                             'size':30,
                                                             'type':'text',
                                                             'value':str(obj.objId)
                                                            }
                                                    },
                                      'obj_startdate':{'type':'input',
                                                     'desc':'Start From Date',
                                                     'value':'',
                                                     'closetag':False,
                                                     'hidden':True,
                                                     'meta':{'class':'span8',
                                                             'id':'obj_startdate',
                                                             'name':'obj_startdate',
                                                             'size':30,
                                                             'type':'text',
                                                             'value':''
                                                            }
                                                    },
                                    })[0]


            buttons = btn_info + btn_tail + btn_photo

            ret += t.add((c,
                         obj.objId,
                         obj.storage.meta['msId'],
                         obj.storage.meta['pcnId'],
                         buttons
                        ))
            c += 1
        ret += t.close()
        return ret

    def get_object(self, environ, start_response):
        w.putHeader(start_response, 200, 'html', 'text')

        #PARSE PARAMS
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        if form.getvalue('obj_fromDisk'):
            fromDisk = True
        else:
            fromDisk = False
        name = form.getvalue('obj_name')
        if not helpers.checkName(name): return ['not_found']

        if fromDisk:
            obj = MSObject.MSObject(MSConfig.config.get('main', 'dbpath'), name)
            obj.open()
        else:
            obj = MSDatabase.getInstance().objs.find(name)
        if (not obj) or obj.storage.txn == 0:
            log.debug('method_get_object: not found %s', name)
            return ['not found']

        ret = []
        t   = w.PatternTable()
        ret += t.open([('#',    {}),
                       ('name', {}),
                       ('msId', {}),
                       ('pcnId',{}),
                      ])
        ret += t.add((1,
                     obj.objId,
                     obj.storage.meta['msId'],
                     obj.storage.meta['pcnId'],
                    ))
        ret += t.close()

        ret += t.open([('option',   {}),
                       ('value',    {}),
                      ])
        for k,v in obj.storage.meta.iteritems():
            ret += t.add((k, v))
        ret += t.add(('txn', obj.storage.txn))
        ret += t.close()
        return ret

    def destroy_object(self, environ, start_response):
        w.putHeader(start_response, 200, 'html', 'text')

        #PARSE PARAMS
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        obj_name = form.getvalue('obj_name')
        if not helpers.checkName(name): return ['not_found']

        obj     = MSObject.MSObject(MSConfig.config.get('main','dbpath'), obj_name)
        obj.open()
        if obj.storage.txn == 0:
            return ['not found']
        obj.storage.destroy()
        return ['success']

    def create_object(self, environ, start_response):
        w.putHeader(start_response, 200, 'html', 'text')

        #PARSE PARAMS
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        name = form.getvalue('obj_name')
        if not helpers.checkName(name): return ['failed - name is incorrect']

        obj     = MSObject.MSObject(MSConfig.config.get('main','dbpath'), name)
        obj.open()
        if obj.storage.txn > 0: return ['failed - already exists']

        obj.storage.meta['msId']    = form.getvalue('obj_msId')
        obj.storage.meta['pcnId']   = form.getvalue('obj_pcnId')
        obj.storage.meta['skey']    = form.getvalue('obj_skey')
        obj.flush()
        return ['success'] 

    def get_object_events(self, environ, start_response):
        w.putHeader(start_response, 200, 'html', 'text')
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)

        if form.getvalue('obj_tail'):
            tail = True
        else:
            tail = False

        if form.getvalue('obj_useapi'):
            useAPI = True
        else:
            useAPI = False

        if form.getvalue('obj_usedb'):
            useDB  = True
        else:
            useDB  = False

        name        = form.getvalue('obj_name')
        if not helpers.checkName(name): return ['not_found']

        reader = None
        if useAPI:
            reader = ReaderMSAPI.Reader(MSConfig.config.get('msapi','url'))
        if useDB:
            reader = ReaderMSDB.Reader(MSConfig.config.get('msdb','host'),
                                       MSConfig.config.get('msdb','port'),
                                       MSConfig.config.get('msdb','dbname'),
                                       MSConfig.config.get('msdb','user'),
                                       MSConfig.config.get('msdb','pwd'))

        startdate   = form.getvalue('obj_startdate')
        if startdate == None:
            startdate = MSDatetime.getToday()[0]
        else:
            startdate = datetime.datetime.strptime(startdate, MSDatetime.TIME_FORMAT_MAGICDB_ORIG)

        obj  = MSDatabase.getInstance().objs.find(name)
        if reader != None:
            obj  = MSObject.MSObject(MSConfig.config.get('main', 'dbpath'), name)
            obj.open()
            if obj.storage.txn == 0:
                log.debug('method_get_object_events: not found %s', name)
                return ['object ' + name + ' not found']
            obj.lock.acquire()
            try:
                reader.getEvents_TIMED(obj, startdate)
            finally:
                obj.lock.release()

        ret = []
        ret += ['<h3>']
        ret += ['object "' + str(obj.objId) + '" since ' + startdate.strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG)]
        ret += ['</h3><br/>']

        if tail:
            t = w.PatternTable('zebra-striped', 'eventTable', 'eventTableBody')
        else:
            t = w.PatternTable('zebra-striped', 'eventTableSortable', 'eventTableBody')

        copied_events = list(obj.events)
        copied_events.reverse()
        ret += t.open([('time', {'class':'header'}),
                       ('message',  {'class':'yellow header'}),
                       ('id',   {'class':'yellow header'}),
                       ('pgsm type', {'class':'blue header'}),
                       ('cid', {'class':'green header'}),
                      ])
        for e in copied_events:
            ret += t.add((  str(e.eventGenerateTime), 
                            MSEvent.eventType(e.contactIdFull, 't') + ' ' + MSEvent.eventCode(e.contactIdFull, 't'), 
                            str(e.eventId), 
                            str(e.pgsmType),
                            str(e.contactIdFull)))
        ret += t.close()

        ret += ['<div class="eventTableSize">%d</div>' % len(obj.events)]
        ret += ['events processed totally at:']
        ret += ['<div class="eventTableUpdTime">' + (datetime.datetime.fromtimestamp(time.time())).strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG) + '</div><br>']
        if tail:
            ret += ['<div class="eventTableStartdate" hidden="true">%s</div>' % str(startdate.strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG))]
            ret += ['<div class="eventTableObjName" hidden="true">' + name + '</div>']
            ret += ['<div class="eventTablePlaceholder" hidden="true"></div>']
            ret += ['<a class="maya2" href="blank">Enable RealTime Monitoring</a><br>']
        if useAPI:
            ret += ['<div class="eventTableMethod" hidden="true">api</div>']
        elif useDB:
            ret += ['<div class="eventTableMethod" hidden="true">db</div>']
        else:
            ret += ['<div class="eventTableMethod" hidden="true"></div>']
        return ret

    def get_object_photo(self, environ, start_response):
        w.putHeader(start_response, 200, 'jpg', 'image')
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        #f = open(MSConfig.config.get('main', 'image') + 'boobs.jpg', 'rb')
        #return [f.read()]
        photo_id    = form.getvalue('photo_id')
        if not photo_id: 
            return ['photo id is not provided']
        photo_id = int(photo_id)

        name        = form.getvalue('obj_name')
        if not w.checkName(name):
            if not name: name = ''
            log.debug('method_get_object_events: incorrect name %s', name)
            return ['object ' + name + ' not found']
        found = None
        obj = MSDatabase.getInstance().objs.find(name)
        obj.lock.acquire()
        try:
            for e in MSObject.genPhotos(obj.events):
                if photo_id == e.eventId:
                    found = e
        finally:
            obj.lock.release()

        if found and found.photoContent: return found.photoContent
        return ['photo ' + str(photo_id) + ' not found']

    def list_object_photos(self, environ, start_response):
        w.putHeader(start_response, 200, 'html', 'text')
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)

        timerange   = MSDatetime.getLastWeek()
        startdate   = form.getvalue('obj_startdate')
        if startdate != None:
            startdate = datetime.datetime.strptime(startdate, MSDatetime.TIME_FORMAT_MAGICDB_ORIG)
            timerange = (startdate, timerange[1])

        name        = form.getvalue('obj_name')
        if not w.checkName(name):
            if not name: name = ''
            log.debug('method_get_object_events: incorrect name %s', name)
            return ['object ' + name + ' not found']
        MSDatabase.getInstance().syncWithSource('msdb', 24*365, 4)
        obj  = MSDatabase.getInstance().objs.find(name)
        obj.lock.acquire()
        try:
            events = list(obj.events)
        finally:
            obj.lock.release()
        images = []
        for e in MSObject.genPhotos(events): 
            images.append(e)
        images.reverse()

        ret = []
        ret += ['<h3>']
        ret += ['object "' + str(obj.objId) + '" photos']
        ret += ['</h3><br/>']
        ret += ['%d' % (len(images))] + ['<br>']

        t = w.PatternTable('zebra-striped', 'photoTable', 'photoTableBody')
        ret += t.open([('id',  {'class':'header'}),
                       ('time',  {'class':'header'}),
                       ('photo',  {'class':'header'}),
                       ('link',  {'class':'header'}),
                      ])
        for e in images:
            if e.photoContent:
                ret += t.add((  str(e.eventId), 
                                str(e.eventGenerateTime), 
                                '<img src="../rawmethod/get_object_photo?obj_name=%s&photo_id=%s">' % (str(obj.objId), str(e.eventId)),
                                '<a href="../rawmethod/get_object_photo?obj_name=%s&photo_id=%s" target="_blank">' % (str(obj.objId), str(e.eventId)),
                            ))
            if e.photoRef:
                ret += t.add((  str(e.eventId), 
                                str(e.eventGenerateTime), 
                                '<img src="../image/%s">' % (str(e.photoRef)),
                                '<a href="../image/%s" target="_blank">' % (str(e.photoRef)),
                            ))
        ret += t.close()
        return ret

    def get_photo_byid(self, environ, start_response):
        w.putHeader(start_response, 200, 'jpg', 'image')
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        photo_id    = form.getvalue('photo_id')
        if not photo_id: 
            return ['photo id is not provided']
        photo_id = int(photo_id)

        reader = ReaderMSDB.Reader(MSConfig.config.get('msdb','host'),
                                   MSConfig.config.get('msdb','port'),
                                   MSConfig.config.get('msdb','dbname'),
                                   MSConfig.config.get('msdb','user'),
                                   MSConfig.config.get('msdb','pwd'))
        reader.connect()
        cursor = reader.connection.cursor()
        query = "SELECT deviceId,eventTime,eventId,image FROM foto WHERE eventId='%s'" % (str(photo_id))
        cursor.execute(query)

        events = []
        events += ReaderMSDB.createEvents(cursor.fetchall(), 4)

        images = []
        for e in MSObject.genPhotos(events): images.append(e)
        images.reverse()

        for i in images:
            if int(i.eventId) == int(photo_id) and i.photoContent:
                return i.photoContent
        return ['not found']

    def list_all_object_photos(self, environ, start_response):
        w.putHeader(start_response, 200, 'html', 'text')

        timerange   = MSDatetime.getLastWeek()
        startdate = datetime.datetime.strptime("2013-01-01 00:00:00", MSDatetime.TIME_FORMAT_MAGICDB_ORIG)
        timerange = (startdate, timerange[1])

        reader = ReaderMSDB.Reader(MSConfig.config.get('msdb','host'),
                                   MSConfig.config.get('msdb','port'),
                                   MSConfig.config.get('msdb','dbname'),
                                   MSConfig.config.get('msdb','user'),
                                   MSConfig.config.get('msdb','pwd'))
        reader.connect()
        cursor = reader.connection.cursor()
        pdb.set_trace()
        query = "SELECT deviceId,eventTime,eventId,image FROM foto WHERE eventTime>'%s'" % (str(timerange[0]))
        cursor.execute(query)

        events = []
        events += ReaderMSDB.createEvents(cursor.fetchall(), 4)

        images = []
        for e in MSObject.genPhotos(events): images.append(e)
        images.reverse()

        ret = []
        ret += ['<h3>']
        ret += ['All object\'s photos']
        ret += ['</h3><br/>']
        ret += ['%d' % (len(images))] + ['<br>']

        t = w.PatternTable('zebra-striped', 'photoTable', 'photoTableBody')
        ret += t.open([('id',  {'class':'header'}),
                       ('time',  {'class':'header'}),
                       ('photo',  {'class':'header'}),
                       ('link',  {'class':'header'}),
                      ])
        for e in images:
            if e.photoContent:
                ret += t.add((  str(e.eventId), 
                                str(e.eventGenerateTime), 
                                '<img src="../rawmethod/get_object_photo?obj_name=%s&photo_id=%s">' % (str(e.objectId), str(e.eventId)),
                                '<a href="../rawmethod/get_object_photo?obj_name=%s&photo_id=%s" target="_blank">' % (str(e.objectId), str(e.eventId)),
                            ))
            if e.photoRef:
                ret += t.add((  str(e.eventId), 
                                str(e.eventGenerateTime), 
                                '<img src="../image/%s">' % (str(e.photoRef)),
                                '<a href="../image/%s" target="_blank">' % (str(e.photoRef)),
                            ))
        ret += t.close()
        return ret

